from django.views import View
from django.contrib.auth import login, logout, authenticate
from django.http import JsonResponse
# 当前目录是指整个项目运行环境，即ｍａｎａｇｅ所在目录（外层目录meiduo_mall）为导包目录之一,但是如果是应用(apps),最好从注册目录开始导包，避免错误。
# 从注册目录ａｐｐｓ开始导包
# from users.models import User
# 相对目录导包　.models
from .models import User, Address
from meiduo_mall.utils.views import LoginRequiredJsonMixin

from django import http
from django_redis import get_redis_connection
from django.conf import settings
from celery_tasks.email.tasks import send_verify_email
from .utils import generate_verify_email_url
from meiduo_mall.utils.secret import SecretOauth
from carts.utils import merge_cart_cookie_to_redis


import json, re
# Create your views here.


# 判断用户名是否重复
class UsernameCountView(View):

    def get(self, request, username):
        # 1.提取参数
        # 2.校验参数
        # 3.业务数据处理 -- 根据用户名统计用户数量

        try:
            count = User.objects.filter(username=username).count()
        except Exception as e:
            return http.JsonResponse({
                "code": 400,
                "errmsg": "数据库错误！"
            })

        # 4.构建响应
        return http.JsonResponse({
            "code": 0,
            "errmsg": "ok",
            "count": count
        })


# 判断手机号是否重复
class MobileCountView(View):

    def get(self, request, mobiles):

        # 1.提取参数
        # 2.校验参数
        try:
            # 3.业务数据处理
            print("注册手机号：", mobiles)
            count = User.objects.filter(mobile=mobiles).count()
        except Exception as e:
            return http.JsonResponse({
                "code": 400,
                "errmsg": "数据库错误!"
            })

        # 4.构建响应
        return http.JsonResponse({
            "code": 0,
            "errmsg": "ok",
            "count": count
        })


# 用户注册
class RegisterView(View):

    def post(self, request):
        # 1.提取参数
        # request.body # 请求体参数，类型是字节对象 b'{"username": xxx....}'
        data = json.loads(request.body.decode()) # 低版本的python中，loads函数需要传入字符串
        # data = json.loads(request.body)

        username = data.get("username")
        password = data.get("password")
        password2 = data.get("password2")
        mobile = data.get("mobile")
        sms_code = data.get("sms_code")
        allow = data.get("allow", False)

        # 2.校验参数
        # 2.1、必要性校验
        # all(): 判断给定的可迭代参数 iterable 中的所有元素是否都为 TRUE，如果是返回 True，否则返回 Fals
        if not all([username, password, password2, mobile, sms_code, allow]):
            return http.JsonResponse({
                "code": 400,
                "errmsg": "缺少必要参数"
            }, status=400)

        # 2.2、约束条件校验
        if not re.match(r"^[0-9a-zA-Z_-]{5,20}$", username):
            return http.JsonResponse({
                "code": 400,
                "errmsg": "用户名格式错误"
            })

        if not re.match(r"^[0-9a-zA-Z]{8,20}$", password):
            return http.JsonResponse({
                "code": 400,
                "errmsg": "密码格式错误"
            })

        # 2次输入密码是否一致
        if password != password2:
            return http.JsonResponse({
                "code": 400,
                "errmsg": "2次密码输入不一样"
            })

        if not re.match(r"^1[3-9]\d{9}$", mobile):
            return http.JsonResponse({
                "code": 400,
                "errmsg": "手机格式有误"
            })

        if not re.match(r"^\d{6}$", sms_code):
            return http.JsonResponse({
                "code": 400,
                "errmsg": "短信验证码格式有误"
            })

        if not isinstance(allow, bool):
            return http.JsonResponse({
                "code": 400,
                "errmsg": "allow格式有误"
            })
        if not allow:
            return http.JsonResponse({
                "code": 400,
                "errmsg": "请求勾选同意协议"
            })

        # 2.3、业务性校验(短信验证校验)
        # 此处填充短信验证码校验逻辑代码
        conn = get_redis_connection("verify_code") # 2
        sms_code_from_redis = conn.get('sms_%s'%mobile)
        if not sms_code_from_redis:
            return http.JsonResponse({"code": 400, "errmsg": "短信验证码失效"})
        if sms_code != sms_code_from_redis.decode():
            return http.JsonResponse({"code": 400, "errmsg": "短信验证码有误"})

        # 3、业务数据处理 —— 新建User模型类对象保存数据库
        try:
            user = User.objects.create_user(
                username=username,
                password=password,
                mobile=mobile
            )
        except Exception as e:
            # logger.error(e)
            print(e)
            return http.JsonResponse({
                "code": 400,
                "errmsg": "数据库写入失败"
            }, status=500)

        # 状态保持 —— 使用session机制，把用户数据写入redis
        login(request, user)

        # 4、构建响应
        response = http.JsonResponse({
            "code": 0,
            "errmsg": "注册成功"
        })
        response.set_cookie("username", username, max_age= 3600 * 24 * 14)
        return response


# 登陆
class LoginView(View):

    def post(self, request):
        # 1.提取参数
        data = json.loads(request.body.decode())
        username = data.get("username")
        password = data.get("password")
        remembered = data.get("remembered")

        # 2.校验参数
        if not all([username, password]):
            return http.JsonResponse({"code": 400, "errmsg": "缺少用户名或密码"}, status=400)
        if not re.match(r"^[0-9a-zA-Z_-]{5,20}$", username):
            return http.JsonResponse({"code": 400, "errmsg": "用户名错误"}, status=400)
        if not re.match(r"^[0-9a-zA-Z]{8,20}$", password):
            return http.JsonResponse({"code": 400, "errmsg": "密码错误"}, status=400)

        # 3.业务数据处理
        # 功能：传统身份认证
        # 参数：request请求对象，username用户和password密码
        # 返回值：验证成功返回用户对象；失败返回None
        '''
        try:
            # get() 若无数据则报错，需错误处理
            user = User.objects.get(username=username)
        except User.DoesNotExist:
            return http.JsonResponse({"code": 400, "errmsg": "用户名错误"}, status=400)

        # 模型类.check_password(password): 验证密码方法
        result = user.check_password(password)
        if not result:
            return http.JsonResponse({"code": 400, "errmsg": "密码错误"}, status=400)
        '''

        print("# 使用Django模型类中authenticate(验证)方法")
        # request: 有可能取头中数据
        user = authenticate(request, username=username, password=password)
        if user is None:
            return http.JsonResponse({"code": 400, "errmsg": "用户名或密码错误"}, status=400)

        # 状态保持
        # 把用户信息保存至session
        # 把session的sessionid存放在cookie
        # 把cookie放到响应中,会随着响应返回给前端浏览器
        login(request, user) # 默认状态保持记录用户数据有效期为2周

        # 3.3 判断是否记住用户
        if remembered != True:
            request.session.set_expiry(0)
        else:
            request.session.set_expiry(None)

        # 利用Cookie记录用户名作页面显示
        # 4.构建响应
        response = http.JsonResponse({"code": 0, "errmsg": "ok"})

        # 合并购物车
        response = merge_cart_cookie_to_redis(request, response)
        response.set_cookie("username", username, max_age=3600 * 24 * 14)
        return response


# 退出
class LogoutView(View):

    def delete(self, request):
        # - 删除用户的登陆信息(删除session数据)；
        logout(request) # 通过request对象获取用户信息，然后在去清除session数据

        # - 删除保存在cookie中的username
        response = http.JsonResponse({"code": 0, "errmsg": "ok"})
        response.delete_cookie("username")
        return response


# class UserInfoView(View):
#
#     def get(self, request):
#         # 判断用户是否登陆
#         # user模型类对象：　user.is_authenticate = True
#         # AnonymousUser 匿名用户对象　user.is_authenticate = False
#         user = request.user
#
#         if not user.is_authenticated:
#             return http.JsonResponse({"code": 400, "errmsg": "用户未登陆"})
#
#         return http.JsonResponse({
#             "code": 0,
#             "errmsg": "ok",
#             "info_data": {
#                 "username": user.username,
#                 "mobile": user.mobile,
#                 "email": user.email
#             }
#         })


# LoginRequiredMixin视图拓展类——拓展视图功能
# 用户中心
class UserInfoView(LoginRequiredJsonMixin, View):

    def get(self, request):
        user = request.user

        return http.JsonResponse({
            "code": 0,
            "errmsg": "ok",
            "info_data": {
                "username": user.username,
                "mobile": user.mobile,
                "email": user.email,
                # 新增返回email_active字段
                "email_active": user.email_active
            }
        })


# LoginRequiredMixin视图拓展类在用户未登陆的时候，,返回的响应是HttpResponseRedirect,返回的数据不是json,而我们需要的返回JsonResponse,所以需要重写
# 1. 文件在meiduo_mall/utils/views.py
# 2. 在View中定义LoginRequiredJsonMixin 继承 LoginRequiredMixin, 重写handle_no_permission　方法 返回未登陆的json
# 3. 在当前UserInfoView中继承 LoginRequiredJsonMixin 类:
#     class UserInfoView(LoginRequiredJsonMixin, View):


# 添加邮箱接口
class EmailView(LoginRequiredJsonMixin, View):

    def put(self, request):
        # 1.提取参数
        data = json.loads(request.body.decode())
        email = data.get("email")

        # 2.校验参数
        if not email:
            return http.JsonResponse({"code": 400, "errmsg": "缺少email参数"})
        if not re.match(r"^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$", email):
            return http.JsonResponse({"code": 400, "errmsg": "email格式错误"})

        # 3.业务处理--
        # 3.1存储邮箱数据
        try:
            user = request.user
            user.email = email
            user.save()
        except Exception as e:
            return http.JsonResponse({"code": 400, "errmsg": "数据库错误"})

        # 3.2 发送验证邮件
        # verify_url分为2个部分：
        # (1)、固定的验证请求前缀：http://www.meiduo.site:8080/success_verify_email.html?token=
        # (2)、查询字符串参数： ?token=<是一个字符串，该字符串是加密后的用户数据>
        # 需要生成记录当前登陆用户信息的token，作为verify_url查询字符串的拼接
        verify_url = generate_verify_email_url(request)
        send_verify_email.delay(email, verify_url=verify_url)

        # 4.构建响应
        return http.JsonResponse({"code":0, "errmsg": "ok"})

# 验证邮箱
class VerifyEmailView(View):

    def put(self, request):
        # 1.提取参数
        token = request.GET.get("token")
        # 2.校验参数
        if not token:
            return http.JsonResponse({'code': 400, 'errmsg': '缺少token'})
        # 3、业务数据处理 —— 解密token值(解密成功则验证邮箱成功；否则验证失败)
        # 3.1、验证token，获取用户信息
        auth = SecretOauth()
        user_info = auth.loads(token)
        if user_info is None:
            return http.JsonResponse({'code': 400, 'errmsg': 'token无效'})
        user_id = user_info.get("user_id")
        email = user_info.get("email")

        try:
            # request.user 为None
            user = User.objects.get(pk=user_id)
            user.email_active = True
            user.save()
        except Exception as e:
            return http.JsonResponse({'code': 400, 'errmsg': '激活失败'})

        return http.JsonResponse({'code': 0, 'errmsg': 'ok'})


# 新增用户收货地址
class CreateAddressView(LoginRequiredJsonMixin, View):

    def post(self, request):
        # 1.提取参数
        user = request.user
        count = Address.objects.filter(user=user, is_deleted=False).count()
        if count >= 20:
            return http.JsonResponse({'code': 400, 'errmsg': '最多可以创建20个收货地址'})

        data = json.loads(request.body.decode())

        receiver = data.get("receiver")
        province_id = data.get("province_id")
        city_id = data.get("city_id")
        district_id = data.get("district_id")
        place = data.get("place")
        mobile = data.get("mobile")
        tel = data.get("tel")
        email = data.get("email")

        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return http.JsonResponse({"code": 400, "errmsg": "缺少必要参数"})

        if not re.match(r'^\w{1,20}$', receiver):
            return http.JsonResponse({'code': 400, 'errmsg': '参数receiver有误'})

        if not re.match(r'^\w{1,50}$', place):
            return http.JsonResponse({'code': 400, 'errmsg': '参数place有误'})

        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.JsonResponse({'code': 400, 'errmsg': '参数mobile有误'})

        if tel:
            if not re.match(r'^\w{1,20}$', tel):
                return http.JsonResponse({'code': 400, 'errmsg': '参数tel有误'})
        if email:
            if not re.match(r'^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$', email):
                return http.JsonResponse({'code': 400, 'errmsg': '参数email有误'})
            if len(email) > 30:
                return http.JsonResponse({'code': 400, 'errmsg': '邮箱长度有误'})

        # 3.业务数据处理－－新建Address模型类保存数据
        try:
            address = Address.objects.create(
                user=user,  # 当前新增地址从属用户
                title=receiver,  # 新增的时候把收件人名称作为默认地址标题
                receiver=receiver,  # 收货人别名
                province_id=province_id,
                city_id=city_id,
                district_id=district_id,
                place=place,
                mobile=mobile,
                # tel=tel if tel else ""
                tel=tel or "",
                email=email or ""
            )
            # 如果当前登陆用户没有设置默认地址，把当前新增的地址作为其默认地
            if not user.default_address:
                user.default_address = address
                user.save()
        except Exception as e:
            return http.JsonResponse({"code": 400, "errmsg": "数据库写入错误"})

        address_dict = {
            "id": address.id,
            "title": address.title,
            "receiver": address.receiver,
            "province": address.province.name, # 省名称
            "city": address.city.name, # 市名称
            "district": address.district.name, # 区名称
            "place": address.place,
            "mobile": address.mobile,
            "tel": address.tel,
            "email": address.email
        }

        return http.JsonResponse({
            'code': 0,
            'errmsg': '新增地址成功',
            'address': address_dict
        })


# 展示用户收货地址
class AddressView(LoginRequiredJsonMixin, View):

    def get(self, request):

        user = request.user

        address_queryset = Address.objects.filter(user=user, is_deleted=False)

        addresses = []
        for address in address_queryset:
            address_dict = {
                "id": address.id,
                "title": address.title,
                "receiver": address.receiver,
                "province": address.province.name,  # 省名称
                "city": address.city.name,  # 市名称
                "district": address.district.name,  # 区名称
                "place": address.place,
                "mobile": address.mobile,
                "tel": address.tel,
                "email": address.email
            }
            if address.id == user.default_address_id:
                addresses.insert(0, address_dict)
            else:
                addresses.append(address_dict)

        return http.JsonResponse({
            'code': 0,
            'errmsg': 'ok',
            'default_address_id': user.default_address_id,
            'addresses': addresses
        })


# 修改用户收货地址
class UpdateDestroyAddressView(LoginRequiredJsonMixin, View):

    def put(self, request, address_id):

        json_dict = json.loads(request.body.decode())

        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')

        # 2、校验参数
        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return JsonResponse({'code': 400,
                                      'errmsg': '缺少必传参数'})

        if not re.match(r'^\w{1,20}$', receiver):
            return JsonResponse({'code': 400, 'errmsg': '参数receiver有误'})

        if not re.match(r'^\w{1,50}$', place):
            return JsonResponse({'code': 400, 'errmsg': '参数place有误'})

        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return JsonResponse({'code': 400, 'errmsg': '参数mobile有误'})

        if tel:
            if not re.match(r'^\w{1,20}$', tel):
                return JsonResponse({'code': 400, 'errmsg': '参数tel有误'})
        if email:
            if not re.match(r'^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$', email):
                return JsonResponse({'code': 400, 'errmsg': '参数email有误'})
            if len(email) > 30:
                return JsonResponse({'code': 400, 'errmsg': '邮箱长度有误'})

        # 3. 业务数据处理，更新模型类数据
        try:
            address = Address.objects.get(pk=address_id)

            address.receiver = receiver
            address.province_id = province_id
            address.city_id = city_id
            address.district_id = district_id
            address.place = place
            address.mobile = mobile
            address.tel = tel or ""
            address.email = email or ""
            address.save()
        except Exception as e:
            return JsonResponse({'code': 400, 'errmsg': '未找到地址'})

        address_dict = {
            "id": address.id,
            "title": address.title,
            "receiver": address.receiver,
            "province": address.province.name,
            "city": address.city.name,
            "district": address.district.name,
            "place": address.place,
            "mobile": address.mobile,
            "tel": address.tel,
            "email": address.email
        }

        return JsonResponse({
            'code': 0,
            'errmsg': 'ok',
            'address': address_dict
        })

    def delete(self, request, address_id):

        user = request.user

        try:
            address = Address.objects.get(pk=address_id)
            address.is_deleted = True
            address.save()
            if address_id == user.default_address_id:
                user.default_address_id = None
                user.save()
        except Exception as e:
            return JsonResponse({'code': 400, 'errmsg': '数据库错误'})

        # 4、构建响应
        return JsonResponse({'code': 0, 'errmsg': 'ok'})


# 设置默认地址
class DefaultAddressView(LoginRequiredJsonMixin, View):

    def put(self, request, address_id):

        try:
            user = request.user
            address = Address.objects.get(pk=address_id, is_deleted=False)
            user.default_address = address
            # user.default_address_id = address_id
            user.save()
        except Exception as e:
            return JsonResponse({'code': 400, 'errmsg': '数据库错误'})

        # 4、构建响应
        return JsonResponse({'code': 0, 'errmsg': 'ok'})


# 设置地址标题
class UpdateTitleAddressView(LoginRequiredJsonMixin, View):

    def put(self, request, address_id):

        user = request.user
        data = json.loads(request.body.decode())
        title = data.get("title")
        # 2、校验参数
        if not title:
            return JsonResponse({'code': 400, 'errmsg': '参数缺失'})
        if len(title) > 20:
            return JsonResponse({'code': 400, 'errmsg': '参数有误'})

        try:
            address = Address.objects.get(pk=address_id, is_deleted=False)
            address.title = title
            address.save()
        except Exception as e:
            return JsonResponse({'code': 400, 'errmsg': '数据库错误'})

        # 4、构建响应
        return JsonResponse({'code': 0, 'errmsg': 'ok'})


# 修改密码
class ChangePasswordView(LoginRequiredJsonMixin, View):

    def put(self, request):
        #
        data = json.loads(request.body.decode())
        old_password = data.get("old_password")
        new_password = data.get("new_password")
        new_password2 = data.get("new_password2")

        #
        if not all([old_password, new_password, new_password2]):
            return JsonResponse({'code':400, 'errmsg':'缺少必传参数'})

        result = request.user.check_password(old_password)
        if not result:
            return JsonResponse({'code':400, 'errmsg':'原始密码不正确'})

        if not re.match(r'^[0-9A-Za-z]{8,20}$', new_password):
            return JsonResponse({'code':400, 'errmsg':'密码最少8位,最长20位'})

        if new_password != new_password2:
            return JsonResponse({"code": 400, "errmsg": "两次输入密码不一致"})

        try:
            request.user.set_password(new_password)
            request.user.save()
        except Exception as e:
            return JsonResponse({'code': 400, "errmsg": "修改密码失败"})

        logout(request)

        response = JsonResponse({"code": 0, "errmsg": "ok"})
        response.delete_cookie("username")
        return response









